package com.hm.ibsm.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.hm.common.def.ActivateDef;
import com.hm.common.def.DataStatusDef;
import com.hm.common.def.MobileDevicesDef;
import com.hm.common.exception.ErrorCode;
import com.hm.common.exception.ServiceException;
import com.hm.common.util.CommonUtil;
import com.hm.common.util.EncryptUtil;
import com.hm.common.util.EncryptUtil.RsaUtil.RsaCallback;
import com.hm.ibsm.auto.IbsmProperties;
import com.hm.ibsm.domain.mg.QMenuDomain;
import com.hm.ibsm.domain.mg.QRoleDomain;
import com.hm.ibsm.domain.mg.QRoleMenuDomain;
import com.hm.ibsm.domain.mg.QUserRoleDomain;
import com.hm.ibsm.domain.mg.RoleMenuDomain;
import com.hm.ibsm.domain.mg.UserAuthTokenDomain;
import com.hm.ibsm.domain.mg.UserDomain;
import com.hm.ibsm.domain.mg.UserRoleDomain;
import com.hm.ibsm.repositry.IMenuRepository;
import com.hm.ibsm.repositry.IRoleMenuRepository;
import com.hm.ibsm.repositry.IRoleRepository;
import com.hm.ibsm.repositry.IUserAuthorTokenRepository;
import com.hm.ibsm.repositry.IUserRepository;
import com.hm.ibsm.repositry.IUserRoleRepositry;
import com.hm.ibsm.util.BeanUtil;
import com.hm.ibsm.vo.auth.AuthUserTokenVo;
import com.hm.ibsm.vo.auth.AuthUserVo;
import com.hm.ibsm.vo.auth.RoleMenuVo;
import com.hm.ibsm.vo.auth.UserAuthTokenVo;
import com.hm.ibsm.vo.auth.UserMenuVo;
import com.hm.ibsm.vo.auth.UserRoleVo;
import com.hm.ibsm.vo.user.UserDetailVo;

/**
 * @author shishun.wang
 * @date 2017年2月8日 下午5:45:06
 * @version 1.0
 * @describe
 */
@Component
@Transactional
public class AuthService {

	@Autowired
	private IUserRepository userRepository;

	@Autowired
	private IUserAuthorTokenRepository userAuthorTokenRepository;

	@Autowired
	private IbsmProperties manageProperties;

	@Autowired
	private IUserRoleRepositry userRoleRepositry;

	@Autowired
	private IRoleMenuRepository roleMenuRepository;

	@Autowired
	private IRoleRepository roleRepository;

	@Autowired
	private IUserRoleRepositry iuserRoleRepositry;

	// @Autowired
	// private JPAQueryFactory jpaQueryFactory;

	@Autowired
	private IMenuRepository menuRepository;

	@PersistenceContext
	EntityManager entityManager;

	public AuthUserVo loginByAccountPwd(String account, String pwd, MobileDevicesDef devices, String clientIp) {
		if (CommonUtil.isAnyEmpty(account, pwd)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "登陆账户、密码允许为空");
		}
		UserDomain userDomain = userRepository.findByAccount(account, ActivateDef.ENABLE);
		if (CommonUtil.isEmpty(userDomain)) {
			throw ServiceException.warn("用户不存在");
		}

		return buildAuthUserInfo(pwd, devices, clientIp, userDomain);
	}

	/**
	 * @param pwd
	 * @param devices
	 * @param clientIp
	 * @param userDomain
	 * @return
	 */
	private AuthUserVo buildAuthUserInfo(String pwd, MobileDevicesDef devices, String clientIp, UserDomain userDomain) {
		if (!userDomain.getPwd().equals(EncryptUtil.Md5.getMD5Code(pwd))) {
			throw ServiceException.warn("密码错误");
		}

		if (manageProperties.getAuthor().isDevAuthEnable()) {// 针对开发环境设置账户
			AuthUserVo accountVo = new AuthUserVo();
			{
				accountVo.setUserName(manageProperties.getAuthor().getDevToken().getUserName());
				accountVo.setToken(manageProperties.getAuthor().getDevToken().getToken());
				accountVo.setSecretKey(manageProperties.getAuthor().getDevToken().getSecretKey());
				accountVo.setLoginTime(new Date());
			}
			return accountVo;
		}

		UserAuthTokenDomain userAuthTokenDomain = new UserAuthTokenDomain();
		if (manageProperties.getAuthor().isSinglePoint()) {
			UserAuthTokenDomain userAuth = userAuthorTokenRepository.findUserAuthTokenByUserId(userDomain.getId(), DataStatusDef.ENABLE);
			if (null != userAuth) {
				userAuthTokenDomain = userAuth;
			}
		}

		RsaCallback rsaCallback = EncryptUtil.RsaUtil.generateEncrypt();
		{
			userAuthTokenDomain.setUserId(userDomain.getId());
			userAuthTokenDomain.setPrivateKey(rsaCallback.getPrivateKey());
			userAuthTokenDomain.setPublicKey(rsaCallback.getPublicKey());
			userAuthTokenDomain.setIpAddress(clientIp);
			userAuthTokenDomain.setDevices(devices);
			userAuthTokenDomain.setLoginTime(new Date());
			userAuthTokenDomain.setAuthTime(new Date());
			userAuthTokenDomain.setValidTime(manageProperties.getAuthor().getTokenValidTime());
		}
		UserAuthTokenDomain authTokenDomain = userAuthorTokenRepository.save(userAuthTokenDomain);

		AuthUserVo accountVo = new AuthUserVo();
		{
			accountVo.setUserName(userDomain.getUserName());
			accountVo.setToken(String.valueOf(EncryptUtil.Base64.encode(authTokenDomain.getId().getBytes())));
			accountVo.setSecretKey(EncryptUtil.RsaUtil.encrypt(authTokenDomain.getPublicKey(), userDomain.getId()));
			accountVo.setLoginTime(authTokenDomain.getLoginTime());
		}

		return accountVo;
	}

	public UserDetailVo loginByAccountPubKey(String account, String privateKey) {
		if (CommonUtil.isAnyEmpty(account, privateKey)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "登陆账户、私钥不允许为空");
		}
		return null;
	}

	public AuthUserVo loginByEmailPwd(String email, String pwd, MobileDevicesDef devices, String clientIp) {
		if (CommonUtil.isAnyEmpty(email, pwd)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "邮箱地址、密码不允许为空");
		}
		UserDomain userDomain = userRepository.findByEmail(email, ActivateDef.ENABLE);
		if (CommonUtil.isEmpty(userDomain)) {
			throw ServiceException.warn("用户不存在");
		}

		return buildAuthUserInfo(pwd, devices, clientIp, userDomain);
	}

	public UserDetailVo loginByEmailPubKey(String email, String privateKey) {
		if (CommonUtil.isAnyEmpty(email, email)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "邮箱地址、私钥不允许为空");
		}
		return null;
	}

	public AuthUserVo loginByMobilePwd(String mobile, String pwd, MobileDevicesDef devices, String clientIp) {
		if (CommonUtil.isAnyEmpty(mobile, pwd)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "手机号码、密码不允许为空");
		}
		UserDomain userDomain = userRepository.findByMobile(mobile, ActivateDef.ENABLE);
		if (CommonUtil.isEmpty(userDomain)) {
			throw ServiceException.warn("用户不存在");
		}

		if (!userDomain.getPwd().equals(pwd)) {
			throw ServiceException.warn("密码错误");
		}

		return buildAuthUserInfo(pwd, devices, clientIp, userDomain);
	}

	public UserDetailVo loginByMobilePubKey(String mobile, String privateKey) {
		if (CommonUtil.isAnyEmpty(mobile, privateKey)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "手机号码、私钥不允许为空");
		}
		return null;
	}

	public boolean setUserRoles(String userId, List<String> roles) {
		if (CommonUtil.isEmpty(userId)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "用户编号不能为空");
		}
		userRoleRepositry.findAll(QUserRoleDomain.userRoleDomain.userId.eq(userId)).forEach(userRoleDomain -> {
			userRoleRepositry.delete(userRoleDomain);
		});

		if (CommonUtil.isEmpty(roles)) {
			return true;
		}

		roles.stream().forEach(roleId -> {
			UserRoleDomain userRole = new UserRoleDomain();
			userRole.setRoleId(roleId);
			userRole.setUserId(userId);
			userRoleRepositry.save(userRole);
		});

		return true;
	}

	public boolean setRoleMenus(String roleId, List<String> menus) {
		if (CommonUtil.isEmpty(roleId)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "角色编号不能为空");
		}

		roleMenuRepository.findAll(QRoleMenuDomain.roleMenuDomain.roleId.eq(roleId)).forEach(roleMenuDomain -> {
			roleMenuRepository.delete(roleMenuDomain);
		});

		if (CommonUtil.isEmpty(menus)) {
			return true;
		}

		menus.stream().forEach(menuId -> {
			RoleMenuDomain roleMenuDomain = new RoleMenuDomain();
			roleMenuDomain.setMenuId(menuId);
			roleMenuDomain.setRoleId(roleId);
			roleMenuRepository.save(roleMenuDomain);
		});

		return false;
	}

	public UserAuthTokenVo getUserAuthTokenInfo(String tokenId) {
		if (CommonUtil.isAnyEmpty(tokenId)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}

		String token = new String(EncryptUtil.Base64.decode(tokenId.toCharArray()));
		UserAuthTokenDomain userAuthTokenDomain = userAuthorTokenRepository.findOne(token);
		if (CommonUtil.isEmpty(userAuthTokenDomain)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}

		UserAuthTokenVo userAuthTokenVo = new UserAuthTokenVo();
		BeanUtil.copyProperties(userAuthTokenDomain, userAuthTokenVo);

		return userAuthTokenVo;
	}

	public AuthUserTokenVo refreshUserAuthTokenInfo(String tokenId, String secretKey, String ip) {
		if (CommonUtil.isAnyEmpty(tokenId, secretKey, ip)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}
		String token = new String(EncryptUtil.Base64.decode(tokenId.toCharArray()));
		UserAuthTokenDomain userAuthTokenDomain = userAuthorTokenRepository.findOne(token);
		if (CommonUtil.isEmpty(userAuthTokenDomain)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}

		AuthUserTokenVo authUserTokenVo = new AuthUserTokenVo();
		if (manageProperties.getAuthor().isDevAuthEnable()) {
			return buildAuthUserTokenInfo(userAuthTokenDomain, authUserTokenVo);
		}

		if (!userAuthTokenDomain.getIpAddress().equals(ip)) {
			RsaCallback rsaCallback = EncryptUtil.RsaUtil.generateEncrypt();
			{
				userAuthTokenDomain.setPrivateKey(rsaCallback.getPrivateKey());
				userAuthTokenDomain.setPublicKey(rsaCallback.getPublicKey());
				userAuthTokenDomain.setIpAddress(ip);
				userAuthTokenDomain.setLoginTime(new Date());
				userAuthTokenDomain.setAuthTime(new Date());
				userAuthTokenDomain.setValidTime(manageProperties.getAuthor().getTokenValidTime());
			}
			userAuthorTokenRepository.save(userAuthTokenDomain);

			authUserTokenVo.setLogout(true);
			return authUserTokenVo;
		}

		// TODO 计算时间,token是否失效
		return buildAuthUserTokenInfo(userAuthTokenDomain, authUserTokenVo);
	}

	/**
	 * @param userAuthTokenDomain
	 * @param authUserTokenVo
	 * @return
	 */
	private AuthUserTokenVo buildAuthUserTokenInfo(UserAuthTokenDomain userAuthTokenDomain, AuthUserTokenVo authUserTokenVo) {
		UserDomain userDomain = userRepository.findOne(userAuthTokenDomain.getUserId());
		if (null != userDomain) {
			authUserTokenVo.setUserName(userDomain.getUserName());
		}
		authUserTokenVo.setToken(String.valueOf(EncryptUtil.Base64.encode(userAuthTokenDomain.getId().getBytes())));
		authUserTokenVo.setSecretKey(EncryptUtil.RsaUtil.encrypt(userAuthTokenDomain.getPublicKey(), userAuthTokenDomain.getId()));
		authUserTokenVo.setLoginTime(userAuthTokenDomain.getLoginTime());

		return authUserTokenVo;
	}

	public String loadCurrentUserId(String tokenId, String secretKey) {
		if (CommonUtil.isAnyEmpty(tokenId, secretKey)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}

		UserAuthTokenVo authTokenInfo = getUserAuthTokenInfo(tokenId);
		try {
			String userId = EncryptUtil.RsaUtil.decrypt(authTokenInfo.getPrivateKey(), secretKey);
			if (!authTokenInfo.getUserId().equals(userId)) {
				throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
			}
			return userId;
		} catch (Exception e) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}
	}

	public boolean checkUserAuthTokenInfo(String tokenId, String secretKey) {
		String userId = loadCurrentUserId(tokenId, secretKey);
		return CommonUtil.isNotEmpty(userRepository.findOne(userId));
	}

	public boolean logout(String tokenId, String secretKey) {
		if (!checkUserAuthTokenInfo(tokenId, secretKey)) {
			throw ServiceException.warning(ErrorCode.NO_DATA_ACCESS);
		}
		if (manageProperties.getAuthor().isDevAuthEnable()) {
			return true;
		}
		String token = new String(EncryptUtil.Base64.decode(tokenId.toCharArray()));
		userAuthorTokenRepository.delete(token);
		return true;
	}

	public List<UserRoleVo> getUserRoles(String userId) {
		if (CommonUtil.isEmpty(userId)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "用户编号不能为空");
		}

		List<UserRoleVo> vos = new ArrayList<UserRoleVo>();
		QUserRoleDomain qUserRoleDomain = QUserRoleDomain.userRoleDomain;
		userRoleRepositry.findAll(qUserRoleDomain.userId.eq(userId).and(qUserRoleDomain.status.eq(DataStatusDef.ENABLE))).forEach(userRoleDomain -> {
			UserRoleVo userRoleVo = new UserRoleVo();
			BeanUtil.copyProperties(userRoleDomain, userRoleVo);
			vos.add(userRoleVo);
		});

		return vos;
	}

	public List<RoleMenuVo> getRoleMenus(String roleId) {
		if (CommonUtil.isEmpty(roleId)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "角色编号不能为空");
		}

		List<RoleMenuVo> vos = new ArrayList<RoleMenuVo>();
		roleMenuRepository.findAll(QRoleMenuDomain.roleMenuDomain.roleId.eq(roleId)).forEach(roleMenuDomain -> {
			RoleMenuVo roleMenuVo = new RoleMenuVo();
			BeanUtil.copyProperties(roleMenuDomain, roleMenuVo);
			vos.add(roleMenuVo);
		});

		return vos;
	}

	public List<UserMenuVo> loadUserMenus(String userId) {
		// TODO 缺少对调用者身份校验
		List<UserMenuVo> menus = new ArrayList<UserMenuVo>();

		QMenuDomain qMenuDomain = QMenuDomain.menuDomain;
		menuRepository.findAll(qMenuDomain.status.eq(DataStatusDef.ENABLE).and(QMenuDomain.menuDomain.parentId.isNull())).forEach(menu -> {
			UserMenuVo userMenu = new UserMenuVo();
			BeanUtil.copyProperties(menu, userMenu);
			userMenu.setMenuId(menu.getId());
			loopMenu(userMenu);

			menus.add(userMenu);
		});

		return menus;
	}

	private void loopMenu(UserMenuVo userMenu) {
		List<UserMenuVo> menus = new ArrayList<UserMenuVo>();

		QMenuDomain qMenuDomain = QMenuDomain.menuDomain;
		menuRepository.findAll(
				qMenuDomain.status.eq(DataStatusDef.ENABLE).and(qMenuDomain.parentId.isNotNull().and(qMenuDomain.parentId.eq(userMenu.getMenuId()))))
				.forEach(menu -> {
					userMenu.setHasChild(true);

					UserMenuVo userMenuVo = new UserMenuVo();
					BeanUtil.copyProperties(menu, userMenuVo);
					userMenuVo.setMenuId(menu.getId());
					loopMenu(userMenuVo);

					menus.add(userMenuVo);
				});

		userMenu.setChilds(menus);
	}

	public String getUserRoles4CheckBox(String userId) {
		StringBuffer buffer = new StringBuffer();
		roleRepository.findAll(QRoleDomain.roleDomain.status.eq(DataStatusDef.ENABLE)).forEach(role -> {

			QUserRoleDomain qUserRoleDomain = QUserRoleDomain.userRoleDomain;
			if (iuserRoleRepositry.exists(
					qUserRoleDomain.userId.eq(userId).and(qUserRoleDomain.status.eq(DataStatusDef.ENABLE)).and(qUserRoleDomain.roleId.eq(role.getId())))) {
				buffer.append("<div class='checkbox'><label><input type='checkbox' checked='true' name='roles' value='" + role.getId() + "'>"
						+ role.getRoleName() + "</label></div>");
			} else {
				buffer.append("<div class='checkbox'><label><input type='checkbox'  name='roles' value='" + role.getId() + "'>" + role.getRoleName()
						+ "</label></div>");
			}
		});
		return buffer.toString();
	}

	public void saveUserRoles(String userId, String[] roles) {
		if (CommonUtil.isEmpty(userId)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_NULL, "用户id不能为空");
		}
		iuserRoleRepositry.delete(iuserRoleRepositry.findAll(QUserRoleDomain.userRoleDomain.userId.eq(userId)));
		if (CommonUtil.isEmpty(roles)) {
			return;
		}
		for (String roleId : roles) {
			UserRoleDomain userRoleDomain = new UserRoleDomain();
			userRoleDomain.setRoleId(roleId);
			userRoleDomain.setUserId(userId);
			iuserRoleRepositry.save(userRoleDomain);
		}

	}
}
